

import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;

/**
 * Class Persons
 * 
 * Manage persons
 * 
 * Operations:
 *   - marry
 *   - divorce
 * 
 * @author Benjamin Zwanzger
 * @version 1.0
 * @date 30.03.2012
 */
public class Person {
    public final static int MARRY_AGE = 18;
    private String _foreName;
    private String _lastName;
    private String _maidenName;
    private GregorianCalendar _birthdate;
    private ArrayList<Marriage> _marriages;
    private boolean _isMarried;
    private Gender _gender;
    
    /**
     * Constructor
     * 
     * @param foreName
     * @param lastName
     * @param gender values are Gender.MALE or Gender.FEMALE
     * @param birthdate 20.02.1970 = new GregorianCalendar(1970, 1, 20)  (Month +1)
     */
    public Person(String foreName, String lastName, Gender gender, GregorianCalendar birthdate) {
        _foreName = foreName;
        _lastName = lastName;
        _birthdate = birthdate;
        _gender = gender;
        _marriages = new ArrayList<Marriage>();
    }
    
    /**
     * Check if person is married
     * @return true if person is married
     */
    public boolean isMarried() {
        return _isMarried;
    }
    
    /**
     * Calculate the age of this person
     * @return age in years
     */
    public int getAge() {
        int age;
        GregorianCalendar now = new GregorianCalendar();
        GregorianCalendar diff = new GregorianCalendar();
        diff.setTimeInMillis(now.getTimeInMillis() - _birthdate.getTimeInMillis());
        /*
         * 01.01.1970 is the basis date
         */
        age = diff.get(Calendar.YEAR) - 1970;
        return age;
    }
    
    /**
     * Add a new marriage to ArrayList and set _isMarried to true
     * Method used by marry();
     * @param m Marriage object
     * @return true if add to ArrayList was successfully
     */
    private boolean addMarriage(Marriage m) {
        if (m.isActive()) {
            _isMarried = true;
            return _marriages.add(m);
        }
        return false;
    }
    
    /**
     * Set last name to maiden name
     * Used by method divorce()
     */
    private void resetLastName() {
        if (_gender == Gender.FEMALE) {
            _lastName = _maidenName;
        }
    }
    
    /**
     * Reset _isMarried status to false
     * Used by method divorce()
     * @return 
     */
    private boolean resetMarriedStatus() {
        if (_isMarried) {
            _isMarried = false;
            return true;
        }
        return false;
    }
    
    /**
     * Marry two Persons
     * Conditions:
     *   - Both persons are unmarried
     *   - Both persons are over 18
     *   - The two persons have different gender
     * @param p Person object to marry
     * @return true if marry was successfully
     */
    public boolean marry(Person p) {
        if (!isMarried() && !p.isMarried()) {
            if ((getAge() >= MARRY_AGE) && (p.getAge() >= MARRY_AGE)) {
                if (_gender != p.getGender()) {
                    boolean rv;
                    Marriage marriage = null;
                    if (_gender == Gender.MALE) {
                        marriage = new Marriage(this, p);
                        p.changeName(_lastName);
                    } else {
                        marriage = new Marriage(p, this);
                        changeName(p.getLastName());
                    }
                    _isMarried = true;
                    _marriages.add(marriage);
                    return p.addMarriage(marriage);
                }
            }
        }
        return false;
    }
    
    /**
     * Divorce two persons
     * Conditions:
     *   - Person is married
     * @return true if divorce was successfully 
     */
    public boolean divorce() {
        if (_isMarried) {
            if (_marriages.get(_marriages.size() - 1).divorce()) {
                Person exPartner = _marriages.get(_marriages.size() - 1).getPartner(this);
                _isMarried = false;
                exPartner.resetMarriedStatus();
                if (_gender == Gender.MALE) {
                    exPartner.resetLastName();
                } else {
                    resetLastName();
                }
                return true;
            }
        }
        return false;
    }
    
    /**
     * Return gender of this person
     * @return gender
     */
    public Gender getGender() {
        return _gender;
    }
    
    /**
     * Set new last name
     * - Check if person is female
     * - Copy last name to maiden name
     * - Set new last name
     */
    private boolean changeName(String newLastName) {
        if (_gender == Gender.FEMALE) {
            _maidenName = _lastName;
            _lastName = newLastName;
            return true;
        }
        return false;
    }
    
    /**
     * Returns last name
     * @return last name
     */
    private String getLastName() {
        return _lastName;
    }
    
    /**
     * Returns all marriages of this person
     * @return array of all marriages
     */
    public Marriage[] getMarriages() {
        Marriage[] marriages = new Marriage[_marriages.size()];
        return _marriages.toArray(marriages);
    }
    
    /**
     * Override toString
     * Information about the person
     * @return information about the person
     */
    @Override
    public String toString() {
        StringBuilder rv = new StringBuilder();
        rv.append(_foreName);
        rv.append(" ");
        rv.append(_lastName);
        rv.append(", ");
        rv.append(_gender);
        rv.append(", ");
        rv.append(_birthdate.get(Calendar.DAY_OF_MONTH));
        rv.append(".");
        rv.append(_birthdate.get(Calendar.MONTH) + 1);
        rv.append(".");
        rv.append(_birthdate.get(Calendar.YEAR));
        rv.append(" (");
        rv.append(getAge());
        rv.append("), ");
        if (_isMarried) {
            rv.append("married with ");
            rv.append(_marriages.get(_marriages.size() - 1).getPartner(this).getName());
        } else {
            rv.append("not married.");
        }
        return rv.toString();
    }
    
    /**
     * Return fore and last name of this person
     * @return fore and last name
     */
    public String getName() {
        return (_foreName + " " + _lastName);
    }
}
